from typing import Dict, Optional, List, Tuple
import time
from collections import OrderedDict
from .page import Page, PageType, DataPage, IndexPage
from utils.parser import Parser


class BufferPoolManager:
    """缓冲池管理器
    
    负责管理内存中的页面缓存，实现页面的换入换出策略
    """
    def __init__(self, pool_size: int = 1024):
        """初始化缓冲池管理器
        
        Args:
            pool_size: 缓冲池大小，即可以同时在内存中缓存的页面数量
        """
        self.pool_size = pool_size
        # 页面ID到页面对象的映射
        self.page_table: Dict[int, Page] = {}
        # 使用LRU策略管理页面置换
        self.lru_cache = OrderedDict()
        # 空闲页面列表
        self.free_list: List[Page] = []
        # 磁盘管理器引用
        self.disk_manager = None
    
    def set_disk_manager(self, disk_manager) -> None:
        """设置磁盘管理器"""
        self.disk_manager = disk_manager

    def get_available_page(self,need_space):
        for _,page in self.page_table:
            if page.get_free_space() > need_space:
                return page


    def new_page(self, page_type: PageType) -> Optional[Page]:
        """
        创建一个新页面
        """
        # 必须有 DiskManager 才能分配新页
        if self.disk_manager is None:
            return None
        # 通过磁盘管理器分配实际的页号（与 Java 的 PageCache.allocate 对齐）
        page_id = self.disk_manager.allocate_page()
        # 创建页面对象
        if page_type == PageType.DATA:
            page = DataPage(page_id)
        elif page_type == PageType.INDEX:
            page = IndexPage(page_id)
        else:
            page = Page(page_id, page_type)
        # 注入反向引用
        page.attach_buffer_pool(self)
        # 放入缓冲池（可能触发淘汰）
        if not self._add_to_buffer_pool(page):
            return None
        # 新页已初始化页头，立即写盘，保持文件与内存一致
        self.disk_manager.write_page(page_id, page.get_data())
        page.set_dirty(False)
        return page
    
    def fetch_page(self, page_id: int) -> Optional[Page]:
        """获取指定ID的页面"""
        # 检查页面是否在缓冲池中
        if page_id in self.page_table:
            # 获取页面
            page = self.page_table[page_id]
            # 更新LRU缓存，存到最后使用的
            self.lru_cache.move_to_end(page_id)
            return page
        # 从磁盘加载页面
        if self.disk_manager is None:
            return None
        # 读取页面数据
        page_data = self.disk_manager.read_page(page_id)
        if page_data is None:
            return None
        # 确定页面类型，那这个就很可疑了，为什么会有
        page_type_value = int.from_bytes(page_data[36:40], byteorder='little')
        # error！last_insert是0,没保存下来
        last_insert=int.from_bytes(page_data[32:36], byteorder='little')
        # 建页面
        try:
            page_type = PageType(page_type_value)
        except ValueError:
            page_type = PageType.DATA  # 默认为数据页
        # 创建页面对象
        if page_type == PageType.DATA:
            page = DataPage(page_id)
        elif page_type == PageType.INDEX:
            page = IndexPage(page_id)
        else:
            page = Page(page_id, page_type)
        # 注入反向引用
        page.attach_buffer_pool(self)
        # 设置页面数据
        page.set_data(page_data)
        page.set_dirty(False)  # 刚从磁盘加载的页面不是脏页
        # 添加到缓冲池
        if not self._add_to_buffer_pool(page):
            return None
        return page

    def unpin_page(self, page_id: int, is_dirty: bool = False) -> bool:
        """解除页面的引用"""
        if page_id not in self.page_table:
            return False
        # 获取页面
        page = self.page_table[page_id]
        # 引用-1
        page.unpin()
        if is_dirty:
            page.set_dirty(True)
        return True

    def flush_page(self, page_id: int) -> bool:
        """将指定页面写入磁盘"""
        # 取页面
        if page_id not in self.page_table:
            return False
        if self.disk_manager is None:
            return False
        page = self.page_table[page_id]
        # 脏页写入
        if page.is_dirty():
            self.disk_manager.write_page(page_id, page.get_data())
            page.set_dirty(False)
        return True
    
    def flush_all_pages(self) -> bool:
        """将所有脏页写入磁盘"""
        if self.disk_manager is None:
            return False
        # 所有脏的都写入
        for page_id, page in self.page_table.items():
            if page.is_dirty():
                self.disk_manager.write_page(page_id, page.get_data())
                page.set_dirty(False)
        return True
    

    
    def _add_to_buffer_pool(self, page: Page) -> bool:
        """将页面添加到缓冲池"""
        # 如果在缓存中
        if page.page_id in self.page_table:
            return True
        # 如果空闲页面位置还有-1，用于控制槽位
        if len(self.free_list) > 0:
            self.free_list.pop()  # 移除一个空闲槽位
        # 否则根据LRU替换
        elif len(self.page_table) >= self.pool_size:
            # 找到一个0引用的作为被替换的id
            victim_page_id = None
            for pid in self.lru_cache:
                if self.page_table[pid].get_pin_count() == 0:
                    victim_page_id = pid
                    break
            #         若无则标识失败
            if victim_page_id is None:
                return False
            # 处理page
            victim_page = self.page_table[victim_page_id]
            # 写回
            if victim_page.is_dirty() and self.disk_manager is not None:
                self.disk_manager.write_page(victim_page_id, victim_page.get_data())
            #     删除缓存
            del self.page_table[victim_page_id]
            del self.lru_cache[victim_page_id]
        #     存入新页面
        self.page_table[page.page_id] = page
        # 会不会有问题
        self.lru_cache[page.page_id] = None
        # 引用+1
        page.pin()
        return True

    def release(self, page: Page) -> bool:
        """释放页面引用"""
        return self.unpin_page(page.get_page_number())

    # 暂时不做删页的功能
    # def delete_page(self, page_id: int) -> bool:
    #     """删除指定页面"""
    #     if page_id not in self.page_table:
    #         # 页面不在缓冲池中，仍需要通知磁盘管理器标记释放
    #         if self.disk_manager is not None:
    #             self.disk_manager.deallocate_page(page_id)
    #         return True
    #     page = self.page_table[page_id]
    #     if page.get_pin_count() > 0:
    #         return False  # 页面仍在使用中，无法删除
    #     # 从缓冲池中移除页面
    #     del self.page_table[page_id]
    #     if page_id in self.lru_cache:
    #         del self.lru_cache[page_id]
    #     # 将页面添加到空闲列表
    #     self.free_list.append(page)
    #     # 同步通知磁盘管理器释放（标记）
    #     if self.disk_manager is not None:
    #         self.disk_manager.deallocate_page(page_id)
    #     return True